<!doctype html>
<meta encoding="utf-8">
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
<style>
  #warning {
    color: red;
    background-color: rgba(255,0,0,0.1);
  }
  .hide {
    display:none;
  }
  .resizeable-map {
    width: 90%;
    height: 40vh;
    box-shadow: 0px 0px 5px 5px rgba(0,0,0,0.75);
    display: flex;
    flex-direction: column;
    resize: both;
    padding: 20px;
  }
  .map-row {
    display: flex;
    flex-grow: 1;
  }
  .map-tile {
    border: 1px solid rgba(0,255,255,0.5);
    flex-grow: 1;
    flex-basis: 0;
    padding: 8px;
  }

</style>
<h1 id="warning" class="hide">ResizeObserver not detected. Demo will not work.</h1>

<h3>Resize Observer component demo</h3>
<div class="resizeable-map"></div>

<p>The canonical use case for ResizeObserver is an HTML component
whose contents change depending on its size.</p>
<p>Without ResizeObserver API detecting changes is problematic. There are many hacks, but there is no performant, clean, way for component to detect changes to its size.</p>
<p>With ResizeObserver API, ResizeObserver.observe() is all that is needed.</p>

<h4>Example 1: Tiled map</h4>
<p>Tiled map is a map that uses tiles to fill its content area. The number of tiles
depends on content size. Resize the window to force map to change number its tiles.</p>
<p>Code is straightforward:</p>
<pre>
function handleMapResize(entry) {
  let TileWidth = 100;
  let TileHeight = 100;
  let columnCount = Math.ceil(entry.contentRect.width / TileWidth);
  let rowCount = Math.ceil(entry.contentRect.height / TileHeight);
  // Add/remove tiles. View source for full example.
}
let ro = new ResizeObserver( entries => {
  for (entry of entries)
    if (entry.target.handleResize)
      entry.target.handleResize(entry);
});
for (el of Array.from(document.querySelectorAll('.resizeable-map'))) {
  el.handleResize = handleMapResize;
  ro.observe(el);
}
</pre>

<script>

// Map adds/remove children based upon its size
function handleMapResize(entry) {
  let TileWidth = 100;
  let TileHeight = 100;
  let columnCount = Math.ceil(entry.contentRect.width / TileWidth);
  let rowCount = Math.ceil(entry.contentRect.height / TileHeight);
  let map = entry.target;
  while (map.children.length > rowCount)
    map.lastElementChild.remove();
  while (map.children.length != rowCount) {
    let row = document.createElement('div');
    row.classList.add('map-row');
    map.appendChild(row);
  }
  for (let r=0; r<rowCount; r++) {
    let rowElement = map.children[r];
    while (rowElement.children.length > columnCount)
      rowElement.lastElementChild.remove();
    for (let c=0; c<columnCount; c++) {
      if (c == rowElement.children.length) {
        let tile = document.createElement('div');
        tile.classList.add('map-tile');
        rowElement.appendChild(tile);
      }
      let tile = rowElement.children[c];
      tile.innerText = r + "," + c;
    }
  }
}

if (!window.ResizeObserver) {
  document.querySelector('#warning').classList.remove('hide');
  throw "No ResizeObserver";
}

let ro = new ResizeObserver( entries => {
  for (entry of entries)
    if (entry.target.handleResize)
      entry.target.handleResize(entry);
});

function initComponents() {
  for (el of Array.from(document.querySelectorAll('.resizeable-map'))) {
    el.handleResize = handleMapResize;
    ro.observe(el);
  }
}

initComponents();

</script>
